package util

import (
	"fmt"
	"golang.org/x/text/cases"
	"golang.org/x/text/language"
	"os"
	"path/filepath"
	"reflect"
	"regexp"
	"sort"
	"strings"
	"text/template"
	"time"
)

//***************************************************
//@Link  https://github.com/thkhxm/tgf
//@Link  https://gitee.com/timgame/tgf
//@QQ群 7400585
//author tim.huang<thkhxm@gmail.com>
//@Description
//2023/4/27
//***************************************************

var autoGenerateAPICodePath, _ = filepath.Abs(".")
var autoGenerateAPICSCodePath = ""
var autoGenerateAPICSCodeNamespace = ""

//func SetAutoGenerateAPICodePath(path string) {
//	var err error
//	autoGenerateAPICodePath, err = filepath.Abs(path)
//	if err != nil {
//		panic(err)
//	}
//	fmt.Printf("设置api代码自动生成路径为 %v", autoGenerateAPICodePath)
//}

func SetAutoGenerateAPICSCode(path, namespace string) {
	var err error
	autoGenerateAPICSCodePath, err = filepath.Abs(path)
	autoGenerateAPICSCodeNamespace = namespace
	if err != nil {
		panic(err)
	}
	fmt.Printf("设置C# api代码自动生成路径为 %v", autoGenerateAPICSCodePath)
}

func getProjectModulePath() string {
	dir, _ := os.Getwd()
	var mod []byte
	var err error
	for i := 0; i < 5; i++ {
		filePath := filepath.Join(dir, "go.mod")
		mod, err = os.ReadFile(filePath)
		if err == nil {
			break
		}
		dir = filepath.Join(dir, "..")
	}
	if err != nil {
		fmt.Println(err)
		return ""
	}

	// 使用正则表达式提取模块路径
	re := regexp.MustCompile(`module\s+([^\s]+)`)
	match := re.FindStringSubmatch(string(mod))
	if len(match) == 2 {
		fmt.Println("模块路径：", match[1])
	}
	return match[1]
}
func GeneratorRPC[T any](moduleName, version string) {
	var t T
	v := reflect.ValueOf(&t)
	ty := v.Type().Elem()
	s := make([]struct {
		Args            string
		Reply           string
		NewReply        string
		MethodName      string
		LowerMethodName string
	}, 0)
	a := struct {
		PackageImports []string
		Apis           []struct {
			Args            string
			Reply           string
			NewReply        string
			MethodName      string
			LowerMethodName string
		}
	}{}
	tt := make(map[string]bool)
	tt["github.com/thkhxm/tgf/rpc"] = true

	for i := 0; i < ty.NumMethod(); i++ {
		m := ty.Method(i)
		// 遍历方法的参数列表
		for j := 0; j < m.Type.NumIn(); j++ {
			// 获取参数类型对象
			argType := m.Type.In(j)
			pkg := argType.PkgPath()
			if argType.Kind() == reflect.Pointer {
				pkg = argType.Elem().PkgPath()
				tt[pkg] = true
			}
		}
		d := struct {
			Args            string
			Reply           string
			NewReply        string
			MethodName      string
			LowerMethodName string
		}{Args: m.Type.In(1).String(), Reply: m.Type.In(2).String(),
			NewReply: m.Type.In(2).String()[1:], MethodName: m.Name, LowerMethodName: strings.ToLower(string(m.Name[0])) + m.Name[1:]}

		var r = regexp.MustCompile("[A-Za-z0-9_]+\\.[A-Za-z0-9_]+\\[(.*)\\]")
		match := r.FindStringSubmatch(d.Args)
		if len(match) > 1 {
			pointIndex := strings.LastIndex(match[1], ".")
			pk := match[1][1:pointIndex]
			l := strings.LastIndex(pk, "/")
			d.Args = "*" + pk[l+1:] + match[1][pointIndex:]
			tt[pk] = true
		}

		match = r.FindStringSubmatch(d.Reply)
		if len(match) > 1 {
			pointIndex := strings.LastIndex(match[1], ".")
			pk := match[1][1:pointIndex]
			l := strings.LastIndex(pk, "/")
			d.Reply = "*" + pk[l+1:] + match[1][pointIndex:]
			tt[pk] = true
		}
		s = append(s, d)
	}
	pi := make([]string, 0)

	for k, _ := range tt {
		if k == "" {
			continue
		}
		pi = append(pi, k)
	}
	sort.Strings(pi)
	if len(s) == 0 {
		return
	}
	a.Apis = s
	a.PackageImports = pi
	packageName := moduleName + "_service"
	tpl := fmt.Sprintf(`
//Auto generated by tgf util
//created at %v
//%v module rpc code, version: %v

package %v

import (
{{range .PackageImports}}	"{{.}}"
{{end}}
)

var (
	{{range .Apis}}
	{{.MethodName}} = rpc.ServiceAPI[{{.Args}}, {{.Reply}}]{
		ModuleName: "%v",
		Name:       "{{.MethodName}}",
		MessageType: "%v."+"{{.MethodName}}",
	}
	{{end}}
)
`, time.Now().String(), moduleName, version, packageName, moduleName, moduleName)
	tm := template.New("apiStruct")
	tp, _ := tm.Parse(tpl)
	fileName := strings.ToLower(moduleName) + ".rpc.service.go"
	dirPath := autoGenerateAPICodePath + string(filepath.Separator) + "generated" + string(filepath.Separator)
	path := dirPath + moduleName + "_service"

	// 创建路径中的所有必要的目录
	err := os.MkdirAll(path, os.ModePerm)
	if err != nil {
		panic(err)
	}
	file, err := os.Create(path + string(filepath.Separator) + fileName)
	if err != nil {
		panic(err)
	}
	defer file.Close()
	tp.Execute(file, a)
	//
	modulePath := getProjectModulePath() + "/generated/" + moduleName + "_service"
	packageName = moduleName + "_rpc"
	tt["context"] = true
	tt[modulePath] = true
	pi = make([]string, 0)
	for k, _ := range tt {
		if k == "" {
			continue
		}
		pi = append(pi, k)
	}
	sort.Strings(pi)
	a.PackageImports = pi
	tpl2 := fmt.Sprintf(`
//Auto generated by tgf util
//created at %v
//%v module rpc code, version: %v

package %v

import (
{{range .PackageImports}}	"{{.}}"
{{end}}
)

{{range .Apis}}
func {{.MethodName}}(ctx context.Context, args {{.Args}}, async bool) (reply {{.Reply}},err error) {
	reply = new({{.NewReply}})
	if async {
		err = rpc.SendNoReplyRPCMessage(ctx, %s_service.{{.MethodName}}.New(args, reply))
	} else {
		reply, err = rpc.SendRPCMessage(ctx, %s_service.{{.MethodName}}.New(args, reply))
	}
	return
}
{{end}}
`, time.Now().String(), moduleName, version, packageName, moduleName, moduleName)
	tm2 := template.New("rpcStruct")
	tp2, _ := tm2.Parse(tpl2)
	fileName2 := strings.ToLower(moduleName) + ".rpc.go"
	path2 := dirPath + moduleName + "_rpc"

	// 创建路径中的所有必要的目录
	err2 := os.MkdirAll(path2, os.ModePerm)
	if err2 != nil {
		panic(err2)
	}
	file2, err2 := os.Create(path2 + string(filepath.Separator) + fileName2)
	if err2 != nil {
		panic(err2)
	}
	defer file2.Close()
	tp2.Execute(file2, a)

	fmt.Println()
	fmt.Printf("rpc generate done . module[%s] version[%s]", moduleName, version)
}

type CSStruct struct {
	PackageImports []string
	Apis           []struct {
		Args       string
		Reply      string
		MethodName string
	}
	ModuleName      string
	ModuleNameUpper string
	PushServices    []string
}

var csStructCache = &CSStruct{}

// GeneratorAPI
// @Description: 生成api文件
// @param ref
func GeneratorAPI[T any](moduleName, version string, pushServices ...string) {
	var t T
	v := reflect.ValueOf(&t)
	ty := v.Type().Elem()
	packageName := moduleName + "_service"
	tt := make(map[string]bool)
	tt["github.com/thkhxm/tgf/rpc"] = true
	goStructCache := &CSStruct{}
	goStructCache.ModuleName = moduleName
	goStructCache.ModuleNameUpper = cases.Title(language.English).String(moduleName)
	csStructCache.ModuleName = moduleName
	csStructCache.ModuleNameUpper = cases.Title(language.English).String(moduleName)
	for i := 0; i < ty.NumMethod(); i++ {
		m := ty.Method(i)
		// 遍历方法的参数列表
		for j := 0; j < m.Type.NumIn(); j++ {
			// 获取参数类型对象
			argType := m.Type.In(j)
			pkg := argType.PkgPath()
			if argType.Kind() == reflect.Pointer {
				pkg = argType.Elem().PkgPath()
				tt[pkg] = true
			}
		}

		d := struct {
			Args       string
			Reply      string
			MethodName string
		}{Args: m.Type.In(1).String(), Reply: m.Type.In(2).String(), MethodName: m.Name}
		var r = regexp.MustCompile("[A-Za-z0-9_]+\\.[A-Za-z0-9_]+\\[(.*)\\]")
		match := r.FindStringSubmatch(d.Args)
		if len(match) > 1 {
			pointIndex := strings.LastIndex(match[1], ".")
			pk := match[1][1:pointIndex]
			l := strings.LastIndex(pk, "/")
			d.Args = "*" + pk[l+1:] + match[1][pointIndex:]
			tt[pk] = true
		}

		match = r.FindStringSubmatch(d.Reply)
		if len(match) > 1 {
			pointIndex := strings.LastIndex(match[1], ".")
			pk := match[1][1:pointIndex]
			l := strings.LastIndex(pk, "/")
			d.Reply = "*" + pk[l+1:] + match[1][pointIndex:]
			tt[pk] = true
		}
		csStructCache.Apis = append(csStructCache.Apis, struct {
			Args       string
			Reply      string
			MethodName string
		}{Args: d.Args, Reply: strings.Split(d.Reply, ".")[1], MethodName: d.MethodName})
		goStructCache.Apis = append(goStructCache.Apis, d)
	}
	pi := make([]string, 0)
	for k, _ := range tt {
		pi = append(pi, k)
	}
	sort.Strings(pi)

	goStructCache.PackageImports = pi
	goStructCache.PushServices = pushServices
	csStructCache.PushServices = append(csStructCache.PushServices, pushServices...)
	tpl := fmt.Sprintf(`
//Auto generated by tgf util
//created at %v

package %v

import (
{{range .PackageImports}}	"{{.}}"
{{end}}
)

var %vService = &rpc.Module{Name: "%v", Version: "%v"}

var (
	{{range .Apis}}
	{{.MethodName}} = rpc.ServiceAPI[{{.Args}}, {{.Reply}}]{
		ModuleName: %vService.Name,
		Name:       "{{.MethodName}}",
		MessageType: %vService.Name+"."+"{{.MethodName}}",
	}
	{{end}}
)
`, time.Now().String(), packageName, cases.Title(language.English).String(moduleName), moduleName, version, cases.Title(language.English).String(moduleName), cases.Title(language.English).String(moduleName))
	tm := template.New("apiStruct")
	tp, _ := tm.Parse(tpl)
	fileName := strings.ToLower(moduleName) + ".api.service.go"
	// 创建路径中的所有必要的目录
	pt := autoGenerateAPICodePath + string(filepath.Separator) + "generated" + string(filepath.Separator) + moduleName + "_service"
	err := os.MkdirAll(pt, os.ModePerm)
	if err != nil {
		panic(err)
	}
	file, err := os.Create(pt + string(filepath.Separator) + fileName)
	if err != nil {
		panic(err)
	}
	defer file.Close()
	tp.Execute(file, goStructCache)

	//
	packageName = moduleName + "_api"
	tpl2 := fmt.Sprintf(`
//Auto generated by tgf util
//created at %v

package %v

const(
	{{range .PushServices}}	{{.}} = "{{.}}" 
	{{end}}
)

`, time.Now().String(), packageName)
	tm2 := template.New("pushStruct")
	tp2, _ := tm2.Parse(tpl2)
	fileName2 := strings.ToLower(moduleName) + ".api.go"
	rt2 := autoGenerateAPICodePath + string(filepath.Separator) + "generated" + string(filepath.Separator) + moduleName + "_api"
	// 创建路径中的所有必要的目录
	err2 := os.MkdirAll(rt2, os.ModePerm)
	if err2 != nil {
		panic(err2)
	}
	file2, err2 := os.Create(rt2 + string(filepath.Separator) + fileName2)
	if err != nil {
		panic(err)
	}
	defer file2.Close()
	tp2.Execute(file2, goStructCache)

	//tp.Execute(os.Stdout, a)
}

func GenerateCSApiService() {
	if autoGenerateAPICSCodeNamespace != "" {

		tplCS := fmt.Sprintf(`
//Auto generated by tgf util
//created at %v

namespace %v
{
    public struct %sServerApi
    {
	{{range .Apis}}
 		public static readonly Api<{{.Reply}}> {{.MethodName}} = new("%s","{{.MethodName}}", false, {{.Reply}}.Parser.ParseFrom);
	{{end}}
	{{range .PushServices}}
		public static readonly string {{.}} = "{{.}}"; 
	{{end}}
	}
	
}
`, time.Now().String(), autoGenerateAPICSCodeNamespace, csStructCache.ModuleNameUpper, csStructCache.ModuleName)
		tmCS := template.New("apiCSStruct")
		tpCS, _ := tmCS.Parse(tplCS)
		fileCSName := csStructCache.ModuleNameUpper + "ServerApi.cs"
		err := os.MkdirAll(autoGenerateAPICodePath, os.ModePerm)
		if err != nil {
			panic(err)
		}
		fileCS, errCS := os.Create(autoGenerateAPICSCodePath + string(filepath.Separator) + fileCSName)
		if errCS != nil {
			panic(errCS)
		}
		defer fileCS.Close()
		tpCS.Execute(fileCS, csStructCache)
	}
}
